package codename.carrot.bomberman.network.client;

import java.util.ArrayList;

import android.util.Log;
import android.widget.Toast;
import codename.carrot.bomberman.AppStorage;
import codename.carrot.bomberman.AppStorage.Direction;
import codename.carrot.bomberman.game.gamemessages.BombDropMessage;
import codename.carrot.bomberman.game.gamemessages.BombermanMessage;
import codename.carrot.bomberman.game.gamemessages.GameInitMessage;
import codename.carrot.bomberman.game.gamemessages.GameOverMessage;
import codename.carrot.bomberman.game.gamemessages.PlayerItemPickupMessage;
import codename.carrot.bomberman.game.gamemessages.PlayerJoinMessage;
import codename.carrot.bomberman.game.gamemessages.PlayerLostLifeMessage;
import codename.carrot.bomberman.game.gamemessages.PlayerMovementMessage;
import codename.carrot.bomberman.game.gamemessages.PlayerQuitMessage;
import codename.carrot.bomberman.game.listener.BombListener;
import codename.carrot.bomberman.game.listener.GameOverListener;
import codename.carrot.bomberman.game.listener.PlayerListener;
import codename.carrot.bomberman.game.listener.PlayerStateListener;
import codename.carrot.bomberman.game.networkobjects.DroidPoint;
import codename.carrot.bomberman.network.MessageHandler.MessageReceiver;
import codename.carrot.bomberman.network.NetworkConnection;
import codename.carrot.bomberman.network.PostOffice;
import codename.carrot.bomberman.network.events.ReceiveEvent;
import codename.carrot.bomberman.network.interfaces.NetworkMessage;
import codename.carrot.bomberman.network.listener.NetworkReceiveListener;

/**
 * The clientPostOffice is the main chain between the network and the game.
 * 
 * As a singleton it is available throughout the game to send and register listeners for
 * in-game message events. 
 * 
 * The clientPostOffice is also used to forward messages to the NetworkConnection.
 * 
 * @author Group A4.
 *
 */
public class ClientPostOffice extends PostOffice implements	NetworkReceiveListener {
	
	/**
	 * String for debugging.
	 */
    private String DEBUG = this.getClass().getSimpleName();
    /**
     * The instance of the post office.
     */
	private static ClientPostOffice clientInstance;
	/**
	 * List of listeners for receiving of bomb events.
	 */
	private ArrayList<BombListener> bombListeners = new ArrayList<BombListener>();
	/**
	 * List of listeners for receiving of player related events (movement etc.).
	 */
	private ArrayList<PlayerListener> playerListeners = new ArrayList<PlayerListener>();
	/**
	 * List of listeners for receiving of status of the player (Dead/Alive, Quit Game... etc).
	 */
	private ArrayList<PlayerStateListener> playerStateListeners = new ArrayList<PlayerStateListener>();
	/**
	 * List of listeners for a game over event.
	 */
	private ArrayList<GameOverListener> gameOverListener = new ArrayList<GameOverListener>();

	/**
	 * Constructs the clientPostOffice privately.
	 */
	private ClientPostOffice() {
		NetworkConnection.getInstance().addReceiveListener(this);
		Log.d(DEBUG, "SET LISTENER");
	}

	@Override
	public void destroy() {
		clientInstance = null;
	}

	/**
	 * Singleton, returns an instance of this object. If it does not exits, it will be created.
	 * @return The instance of ClientPostOffice.
	 */
	public static ClientPostOffice getInstance() {
		if (clientInstance == null)
			clientInstance = new ClientPostOffice();
		return clientInstance;
	}
	
	/**
	 * Add a listener for game over events.
	 * @param listener The listener to be added.
	 */
	public void addGameOverListener(GameOverListener listener) {
		gameOverListener.add(listener);
	}

	/**
	 * Remove a listener for game over events.
	 * @param listener The listener to be removed.
	 */
	public void removeGameOverListener(BombListener listener) {
		gameOverListener.remove(listener);
	}	

	/**
	 * Add a listener for bomb events.
	 * @param listener The listener to be added.
	 */
	public void addBombListener(BombListener listener) {
		bombListeners.add(listener);
	}

	/**
	 * Add a remove for bomb events.
	 * @param listener The listener to be removed.
	 */
	public void removeBombListener(BombListener listener) {
		bombListeners.remove(listener);
	}	

	/**
	 * Add a listener for player events.
	 * @param listener The listener to be added.
	 */
	public void addPlayerListener(PlayerListener listener) {
		playerListeners.add(listener);
	}

	/**
	 * Remove a listener for player events.
	 * @param listener The listener to be removed.
	 */
	public void removePlayerListener(PlayerListener listener) {
		playerListeners.remove(listener);
	}

	/**
	 * Add a listener for player state events.
	 * @param listener The listener to be added.
	 */
	public void addPlayerStateListener(PlayerStateListener listener) {
		playerStateListeners.add(listener);
	}

	/**
	 * Remove a listener for player state events.
	 * @param listener The listener to be removed.
	 */
	public void removePlayerStateListener(PlayerStateListener listener) {
		playerStateListeners.add(listener);
	}

	/**
	 * Sort incoming messages and distribute to the right listeners.
	 */
	@Override
	protected void sortMessage(ReceiveEvent e) {
		// SORT MESSAGES HERE.
		NetworkMessage message = e.getMessage();
		BombermanMessage bomberMessage = null;
		
		if (message instanceof BombermanMessage)
			bomberMessage = (BombermanMessage) message;
		if (bomberMessage == null)
			return;
/*		if (message instanceof PlayerMessage) {
			PlayerMessage pm = (PlayerMessage) message;
			for (PlayerListener l : playerListeners) {
				if (pm.hasDied())
					l.onPlayerDied(pm.getPlayerId());
				if (pm.hasMoved())
					l.onPlayerMoved(pm.getPlayerId(),
							pm.getMovedFromXcoorMap(),
							pm.getMovedFromYcoorMap(), pm.getDirection());
			}
		} */

		if (message instanceof PlayerJoinMessage) {
			Log.d(DEBUG, "Received PlayerJoinMessage");
			// Only the gamelobbylistener that should receive this message
			for (PlayerStateListener gll : playerStateListeners) {
				gll.onPlayerJoin((PlayerJoinMessage) message,	e.getClientid());
			}
		}
		
		if (message instanceof PlayerQuitMessage) {
			Log.d(DEBUG, "Received playerquitmessage" + ((PlayerQuitMessage)message).getPlayerId());
	         // Also, regular player listeners need to be notified
            for(PlayerListener pl : playerListeners) {
                pl.onPlayerQuit(((PlayerQuitMessage)message).getPlayerId());
            }
			for (PlayerStateListener gll : playerStateListeners) {
				gll.onPlayerQuit((PlayerQuitMessage) message);
			}
		}

		if (message instanceof PlayerItemPickupMessage) {
			Log.d(DEBUG, "Received playeritempickupmessage");
			if (!playerListeners.isEmpty()) {
				PlayerItemPickupMessage pi = (PlayerItemPickupMessage) message;
				for (PlayerListener p : playerListeners) {
					DroidPoint pos = pi.getPosition();
					p.onPlayerItemPickup(pi.getPlayerId(), pos.x, pos.y);
				}
			}
		}
		
		if(message instanceof PlayerLostLifeMessage) {
		    Log.d(DEBUG, "Received playerLostLifeMessage");
		    if(!playerListeners.isEmpty()) {
		        PlayerLostLifeMessage pl = (PlayerLostLifeMessage) message;
		        for(PlayerListener p : playerListeners) {		            
		            p.onPlayerLostLife(pl.getPlayerId(), pl.getKilledByPlayerId(), pl.getNrLivesLeft());
		        }
		    }
		}

		if (message instanceof GameInitMessage) {
			// Initialization of the game should only be sent to the gamelobby
			// listener		    
		    Log.d(DEBUG, "GameInitMessage in ClientPostOffice");
			for (PlayerStateListener gll : playerStateListeners) {
				gll.onInitBeforegame((GameInitMessage) message);
			}
		}

		if (message instanceof BombDropMessage) {
			if (!bombListeners.isEmpty()) {
				BombDropMessage bm = (BombDropMessage) message;
				DroidPoint bombPos = bm.getPosition();
				for (BombListener pl : bombListeners) {
					pl.onPlayerDroppedBomb(bm.getPlayerId(), bombPos.x,
							bombPos.y);
				}
			}
		}

		if (message instanceof PlayerMovementMessage) {
			if (!playerListeners.isEmpty()) {
				PlayerMovementMessage pm = (PlayerMovementMessage) message;
				int xCoor, yCoor;
				xCoor = pm.getxMapCoor();
				yCoor = pm.getyMapCoor();
				for (PlayerListener pl : playerListeners) {
					if (pm.isMoving()) {
						pl.onPlayerMoved(pm.getPlayerId(), xCoor, yCoor, Direction.values()[pm.getDirection()]);
					} else {
						pl.onPlayerStopsMoving(pm.getPlayerId(), xCoor, yCoor);
					}
				}
			}
		}
		if (message instanceof GameOverMessage) {
			GameOverMessage gom = (GameOverMessage) message;
			for (GameOverListener gol: gameOverListener){
				gol.onGameEnd(gom.getWinner());
			}
		}
	}
	
	/**
	 * Send a message over the network as a client.
	 */
	public void sendMessage(BombermanMessage message) {
	    NetworkConnection.getInstance().sendMessage(message, 0, -1, MessageReceiver.INDEPENDENT);
	}

	@Override
	public void onMessageReceive(ReceiveEvent e) {
		Log.d(DEBUG, "onMessageReceive FROM " + e.getClientid());
		sortMessage(e);
	}

	@Override
	protected void sendMessage(BombermanMessage message, int clientFrom) {}
}
